package symbol;

import java.util.Dictionary;
import java.util.Enumeration;
import java.util.Hashtable;

public class ClassTable {

	private Dictionary<Symbol,MethodTable> methods;
	private Dictionary<Symbol,Symbol> fields;
	private ClassTable extendsTable;
	private Symbol extendsClass;
	private Symbol currentMethod;
	private Symbol className;	
	
	public ClassTable( Symbol nomedaClasse , Symbol classExtends ){
		methods = new Hashtable<Symbol, MethodTable>();
		fields = new Hashtable<Symbol, Symbol>();
		className = nomedaClasse;
		extendsClass = classExtends;
		
	}
	
	public Symbol getCurrentMethod(){
		return currentMethod;
	}
	
	public MethodTable methodCurrent()
	{
		if( currentMethod == null ) return null;
		return methods.get(currentMethod);
	}
	
	public Symbol className(){
		return className;
	}
	
	public void putMethod( MethodTable metodo ) throws Exception
	{		
		if( methods.get(metodo.getId()) == null ){
			methods.put(metodo.getId(), metodo);
		}else{
			throw new Exception("Redefinicao do metodo: " + metodo.getId().toString());
		}
	}

	public void putFields( Symbol id, Symbol tipo) throws Exception{
				
		if( fields.get(id) != null ){
			throw new Exception("Dupla declaracao de variavel: " + id.toString() );
		}
		fields.put(id, tipo);		
	}
	
	public void setExtendClass( String et ){
		this.extendsClass = Symbol.symbol(et);
	}
	
	public void setExtendsTable(ClassTable et ){
		this.extendsTable = et;
	}
	
	public Symbol setCurrentMethod(Symbol id) {
		
		if( id == null ){
			this.currentMethod = null;
			return null;
		}
		
		if( methods.get(id) == null){
			if( extendsClass != null)
				currentMethod = extendsTable.setCurrentMethod(id);
			else return null;
		}else{
			currentMethod = id;
		}
		
		return currentMethod;
	}
	
	public Symbol methodType() throws Exception{
		
		if( currentMethod == null )
			throw new Exception("Nenhum metodo encontrado");
		
		MethodTable tabela = methods.get(currentMethod);
		
		if( tabela == null ) 
			tabela = extendsTable.methods.get(currentMethod);
		
		return tabela.returnType();
	}
	
	public Symbol getVar(Symbol id) throws Exception{
		Symbol varType = null;
		
		// Busca a variável no método corrente
		if( currentMethod != null )	{	
			MethodTable tabela = methods.get(currentMethod);
			
			// Busca a variável na classe extendida se for necessária
			if( tabela == null ) tabela = extendsTable.methods.get(currentMethod);
			varType = tabela.getType(id);	
		}
		
		// Busca a variável como propriedades dessa classe
		if( varType == null)
			varType = this.fields.get(id);
		
		// Busca a variável nas propriedades da classe extendida
		if( varType == null && this.extendsTable != null )
			varType = this.extendsTable.getVar(id);
		
		// Variável não encontrada em nenhuma das tentativas anteriores
		if( varType == null)
			throw new Exception(id.toString());
		
		return varType;
	}	
	
	public void imprimeClasse(){
		
		System.out.println("Atributos");
		for (Enumeration<Symbol> m=this.fields.keys(); m.hasMoreElements(); )
		{
			System.out.println(" --- " + m.nextElement().toString());
		}				
		 System.out.println("Metodos");
		for (Enumeration<Symbol> m=this.methods.keys(); m.hasMoreElements(); )
		{
			Symbol s = m.nextElement();
			System.out.println(" --- " + s.toString());
			methods.get(s).imprimeLocals();
		}				

	}
	
	public Symbol extendClass(){
		return extendsClass;
	}
	
}
